/*
	Project:	CoreEngine
	Author:		Dino Bojadzhievski
	File:		Vector3

	Defines a three-dimensional vector.
	Non-generic, non-polymorphic.
	Extends nothing.

	All angles given in degrees.
	Defines cross product.
*/

#pragma once

#include <math.h>
#include "Vector2.h"
#include "Enums.h"
#include "../CoreTypes.h"

namespace CoreMath
{
	struct Vector3
	{
		/* Data. */

		real i, j, k;

		/* Constructors. */

		Vector3() {}

		Vector3(real i, real j, real k = LOCATION)
		{
			this->i = i;
			this->j = j;
			this->k = k;
		}

		Vector3(const Vector3& toCopy)
		{
			this->i = toCopy.i;
			this->j = toCopy.j;
			this->k = toCopy.k;
		}

		Vector3(const Vector2& fromVector2)
		{
			this->i = fromVector2.i;
			this->j = fromVector2.j;
			this->k = 0;
		}

		/* Operators. */

		inline Vector3 operator=(const Vector3& toAssign)
		{
			this->i = toAssign.i;
			this->j = toAssign.j;
			this->k = toAssign.k;
			return *this;
		}

		inline bool operator==(const Vector3& toCompare) const
		{
			return (this->i == toCompare.i) && (this->j == toCompare.j) && (this->k == toCompare.k); 
		}

		inline bool operator!=(const Vector3& toCompare) const
		{
			return !(this == &toCompare); 
		}

		inline Vector3 operator*(const real multiplier) const
		{
			return Vector3(this->i * multiplier, this->j * multiplier, this->k * multiplier);
		}

		inline Vector3 operator/(const real divisor) const
		{
			return Vector3(this->i / divisor, this->j / divisor, this->k / divisor);
		}

		inline Vector3 operator+(const Vector3& toAdd) const
		{
			return Vector3(this->i + toAdd.i, this->j + toAdd.j, this->k + toAdd.k);
		}

		inline Vector3 operator-(const Vector3& toSubtract) const
		{
			return Vector3(this->i - toSubtract.i, this->j - toSubtract.j, this->k - toSubtract.k);
		}

		inline Vector3 operator-() const
		{
			return *this * -1;
		}

		inline Vector3 operator+=(const Vector3 toAdd)
		{
			*this = *this + toAdd;
			return *this;
		}

		/* Common vector operations. */ 

		inline real Magnitude(void) const
		{
			return sqrt(this->SqrMagnitude());
		}

		inline real SqrMagnitude(void) const
		{
			return (this->i * this->i) + (this->j * this->j) + (this->k * this->k);
		}

		inline void Normalize(void)
		{
			real vectMagnitude = this->Magnitude();
			
			if (!vectMagnitude)
				return;

			this->i /= vectMagnitude;
			this->j /= vectMagnitude;
			this->k /= vectMagnitude;
		}

		inline static Vector3 Normalize(const Vector3& toNormalize)
		{
			real vectMagnitude = toNormalize.Magnitude();
			return vectMagnitude != 0 ? Vector3(toNormalize.i / vectMagnitude, toNormalize.j / vectMagnitude, toNormalize.k / vectMagnitude) : toNormalize;
		}

		inline real DotProduct(const Vector3& v1, const Vector3& v2)
		{
			real DotProduct = 0;
			DotProduct += (v1.i * v2.i);
			DotProduct += (v1.j * v2.j);
			DotProduct += (v1.k * v2.k);

			return DotProduct;
		}

		inline Vector3 VectorProduct(const Vector3& u, const Vector3& v)
		{
			real i = (i = u.j - v.k) - (v.j * u.k);
			real j = -(u.i * v.k) + (v.i * u.k);
			real k = (u.i  * v.j) - (u.j * v.i);

			return Vector3(i, j, k);
		}

		inline real AngleBetween(Vector3 v1, Vector3 v2)
		{
			v1.Normalize();
			v2.Normalize();
			real product = DotProduct(v1, v2);
			return acos(product);
		}

		/* Arbitrary vector utilities. Mostly, convenience functions */

		inline bool IsLocation()
		{
			return this->k == LOCATION;
		}

		inline bool IsDirection()
		{
			return this->k == DIRECTION;
		}
	};
}
